package ags.muse.recon;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import robocode.Event;
import robocode.RobotDeathEvent;
import robocode.ScannedRobotEvent;
import robocode.StatusEvent;

import ags.muse.base.Rules;
import ags.muse.recon.events.*;

public class RobotList {
	private final Rules rules;
	private final HashMap<String, Robot> robots;
	private final HashMap<String, EnemyRobot> enemies;
	private final HashMap<String, Robot> dead;
	private final SelfRobot self;
    private boolean foundAll = false;
	
	public boolean haveFoundAll() {
        return foundAll;
    }

	public RobotList(Rules rules) {
		this.rules = rules;
		this.robots = new HashMap<String, Robot>();
		this.enemies = new HashMap<String, EnemyRobot>();
		this.dead = new HashMap<String, Robot>();
		this.self = new SelfRobot(rules);
		this.robots.put(rules.NAME, this.self);
	}

	public Collection<Robot> getRobots() {
		return Collections.unmodifiableCollection(robots.values());
	}

	public Collection<EnemyRobot> getEnemies() {
		return Collections.unmodifiableCollection(enemies.values());
	}
	
	public Collection<Robot> getDead() {
		return Collections.unmodifiableCollection(dead.values());
	}
	
	public Robot getRobot(String name) {
		return robots.get(name);
	}
	
	public EnemyRobot getEnemy(String name) {
		return enemies.get(name);
	}

	public SelfRobot getSelf() {
		return self;
	}

	public void handleEvents(List<Event> events) {
		// Create recon event list
		ArrayList<ReconEvent> reconEvents = new ArrayList<ReconEvent>();
		ArrayList<String> deadBots = new ArrayList<String>();
		SelfReconEvent selfEvent = null;
		for (Event e : events) {
			if (e instanceof StatusEvent) {
				selfEvent = new SelfReconEvent(rules, (StatusEvent)e);
				reconEvents.add(selfEvent);
			}
		}
		for (Event e : events) {
			if (e instanceof ScannedRobotEvent) {
				if (selfEvent == null) {
					throw new IllegalStateException("Got ScannedRobotEvent before StatusEvent.");
				}
				reconEvents.add(new ScanReconEvent(selfEvent, (ScannedRobotEvent)e));
			}
			else if (e instanceof RobotDeathEvent) {
				deadBots.add(((RobotDeathEvent)e).getName());
			}
		}
		
		// Process the recon events
		for (ReconEvent e : reconEvents) {
			if (e instanceof SelfReconEvent) {
				// Update scan of self
				self.update((SelfReconEvent)e);
			}
			else if (e instanceof ScanReconEvent) {
				// Update robots
				ScanReconEvent event = (ScanReconEvent)e;
				EnemyRobot robot = enemies.get(event.getName());
				if (robot == null && !dead.containsKey(event.getName())) {
					robot = new EnemyRobot(rules, self, event.getName());
					enemies.put(event.getName(), robot);
					robots.put(event.getName(), robot);
				}
				robot.update(event);
			}
		}
		
		// Remove robots that died
		for (String botname : deadBots) {
			Robot robot = robots.get(botname);
			if (robot != null) {
				dead.put(botname, robot);
			}
			robots.remove(botname);
			enemies.remove(botname);
		}
		
		// Create map of distances
		Robot[] robot_array = robots.values().toArray(new Robot[0]);
		for (Robot r : robot_array) {
		    r.clearDistances();
		}
		for (int i=0; i<robot_array.length; i++) {
			for (int j=i+1; j<robot_array.length; j++) {
				double distance = robot_array[i].getLocation().distance(robot_array[j].getLocation());
				robot_array[i].setDistance(robot_array[j].getName(), distance);
				robot_array[j].setDistance(robot_array[i].getName(), distance);
			}
		}
		
		// Track if we've found every bot
		if (!foundAll && (getEnemies().size() + getDead().size()) >= rules.ENEMIES) {
		    foundAll = true;
		}
	}
}
